#include <ir/visitor.h>
#include <type.h>

namespace ir {

  static void erase_comp_unit(CompUnit & comp_unit) {
    for(auto p : comp_unit.func_list) {
      delete p;
      p = nullptr;
    }
  }

  Visitor::Visitor(CompUnit * comp_unit) {
    Assert(comp_unit != nullptr, "comp_unit is null!");
    erase_comp_unit(*comp_unit);
    this->comp_unit = comp_unit;

    this->current_block = nullptr;
    this->current_exp_reg = nullptr;
    this->current_array_single_index_reg = nullptr;
  }

  void Visitor::visit(ast::NodeCompUnit & comp_unit) {
    this->genarator = std::make_unique<Genarator>(this->comp_unit);
    this->iden_stack.new_stack(); // global stack
    for(auto & global : comp_unit.global_list) {
      if(auto func_def = std::get_if<std::unique_ptr<ast::NodeFuncDef>>(&global)) {
        (*func_def)->accept(*this);
      } else if(auto global_decl = std::get_if<std::unique_ptr<ast::NodeGlobalDecl>>(&global)) {
        (*global_decl)->accept(*this);
      } else {
        Panic("undefined");
      }
    }
    this->iden_stack.delete_last_stack();
  }

  void Visitor::visit(ast::NodeGlobalDecl & global_decl) {
    this->current_type = global_decl.type;
    global_decl.global_defs->accept(*this);
  }

  void Visitor::visit(ast::NodeGlobalDefs & global_defs) {
    for(auto & global_def : global_defs.global_def_list) {
      global_def->accept(*this);
    }
  }

  void Visitor::visit(ast::NodeGlobalDef & global_def) {
    GlobalReg * global_reg = this->genarator->gen_inst_global_alloc(this->current_type,
        global_def.iden, global_def.int_number);
    this->iden_stack.push(global_def.iden, global_reg);
  }

  void Visitor::visit(ast::NodeConstDecl & const_decl) {
    this->current_type = const_decl.type;
    const_decl.const_defs->accept(*this);
  }

  void Visitor::visit(ast::NodeConstDefs & const_defs) {
    for(const auto & const_def : const_defs.const_def_list) {
      const_def->accept(*this);
    }
  }

  void Visitor::visit(ast::NodeConstDef & const_def) {
#ifdef DEBUG
    IdenStackResult iden_stack_result = this->iden_stack.get(const_def.iden);
#endif
    Assert(iden_stack_result.tag == IdenStackResult::NONE,
        "Identifier redefined %s", const_def.iden.c_str());
    this->iden_stack.push(const_def.iden, const_def.int_number);
  }

  void Visitor::visit(ast::NodeVarDecl & var_decl) {
    this->current_type = var_decl.type;
    var_decl.var_defs->accept(*this);
  }

  void Visitor::visit(ast::NodeVarDefs & var_defs) {
    for(const auto & var_def : var_defs.var_def_list) {
      var_def->accept(*this);
    }
  }

  void Visitor::visit(ast::NodeVarDef & var_def) {
#ifdef DEBUG
    IdenStackResult iden_stack_result = this->iden_stack.get(var_def.iden);
    Assert(iden_stack_result.tag == IdenStackResult::NONE,
        "Identifier redefined %s", var_def.iden.c_str());
#endif

    Reg * new_reg = nullptr;
    if(var_def.stage == ast::NodeVarDef::Stage::EXP_NOT_INIT) {
      // just alloc
      new_reg = this->genarator->gen_inst_alloc(*(this->current_block),
            this->current_type);

      // push iden to stack
      this->iden_stack.push(var_def.iden, new_reg);
    } else if(var_def.stage == ast::NodeVarDef::Stage::EXP_INIT) {
      // alloc and push iden to stack
      new_reg = this->genarator->gen_inst_alloc(*(this->current_block),
            this->current_type);
      this->iden_stack.push(var_def.iden, new_reg);

      // get current_exp_reg
      var_def.exp->accept(*this);
      Assert(this->current_exp_reg != nullptr, "current_exp_reg is nullptr");
      this->genarator->gen_inst_store(*(this->current_block),
          new_reg, this->current_exp_reg);
    } else if(var_def.stage == ast::NodeVarDef::Stage::ARR_ZERO_INIT) {
      // const: get current_array
      var_def.array_const->accept(*this);
      Assert(this->current_array != nullptr, "current_array is nullptr");
      new_reg = this->genarator->gen_inst_array_alloc(*(this->current_block), this->current_type,
          this->current_array->all_size());
      this->iden_stack.push(var_def.iden, new_reg, this->current_array);

      // init array with zero
      Reg * zero_reg = this->genarator->gen_inst_imm(*(this->current_block), VarType::i32, 0);
      int count = this->current_array->all_size();
      for(int i = 0; i < count; ++i) {
        Reg * index_reg = this->genarator->gen_inst_imm(*(this->current_block), VarType::i32, i);
        this->genarator->gen_inst_array_store(*(this->current_block),
            new_reg, zero_reg, index_reg);
      }
    } else if(var_def.stage == ast::NodeVarDef::Stage::ARR_INIT) {
      // const: get current_array
      var_def.array_const->accept(*this);

      // generate array alloc and push array_info to stack
      new_reg = this->genarator->gen_inst_array_alloc(*(this->current_block),
          this->current_type, this->current_array->all_size());
      Assert(this->current_array != nullptr, "current_array is nullptr");
      this->iden_stack.push(var_def.iden, new_reg, this->current_array);

      // init this->current_array_depth and index
      this->current_array_depth.clear();
      for(int i = 0; i < this->current_array->depth_count(); i++) {
        this->current_array_depth.push_back(0);
      }
      this->current_array_depth_index = 0;

      // set current array reg for NodeArrayInit
      this->current_array_reg = new_reg;
      var_def.array_init->accept(*this);
    } else if(var_def.stage == ast::NodeVarDef::Stage::ARR_NOT_INIT) {
      // alloc array and push array_info to stack
      var_def.array_const->accept(*this);
      Assert(this->current_array != nullptr, "current_array is nullptr");
      new_reg = this->genarator->gen_inst_array_alloc(*(this->current_block),
          this->current_type, this->current_array->all_size());
      this->iden_stack.push(var_def.iden, new_reg, this->current_array);
    } else {
      Panic("???");
    }
  }

  void Visitor::visit(ast::NodeArrayConst & array_const) {
    // get array and set current_array
    ArrayInfo * array_info = this->genarator->create_array(*(this->current_func),
        array_const.int_number_list);
    this->current_array = array_info;
  }

  // set current_array_single_index_reg
  void Visitor::visit(ast::NodeArray & array) {
    std::vector<Reg*> res_list;
    for(auto & exp : array.exp_list) {
      // get current_exp_reg
      exp->accept(*this);
      Reg * src = this->genarator->gen_inst_imm(*(this->current_block),
          VarType::i32, this->current_array->arr_size_at(res_list.size()));
      Reg * res = this->genarator->gen_inst_mul(*(this->current_block), this->current_exp_reg, src);
      res_list.push_back(res);
    }
    while(res_list.size() > 1) {
      Reg * src1 = res_list.back();
      res_list.pop_back();
      Reg * src2 = res_list.back();
      res_list.pop_back();
      Reg * rd = this->genarator->gen_inst_add(*(this->current_block), src1, src2);
      res_list.push_back(rd);
    }
    this->current_array_single_index_reg = res_list.at(0);
  }

  void Visitor::visit(ast::NodeArrayInit & array_init) {
    using namespace std;
    for(auto & init_item : array_init.init_list) {
      if(auto * exp = get_if<unique_ptr<ast::NodeExp>>(&init_item)) {
        (*exp)->accept(*this);
        // carry all bit befor current_array_depth and check outmemory in array
        for(int i = this->current_array_depth.size()-1; i >= 0; --i) {
          if(this->current_array_depth.at(i) >= this->current_array->depth_size_at(i)) {
            Assert(i != 0, "array too long:%d", this->current_array_depth.at(i));
            this->current_array_depth[i] = 0;
            this->current_array_depth[i-1]++;
          }
        }
        Reg * index_reg = this->genarator->gen_inst_imm(*(this->current_block),
            VarType::i32, this->current_array->get_single_index(this->current_array_depth));
        this->genarator->gen_inst_array_store(*(this->current_block),
            this->current_array_reg, this->current_exp_reg, index_reg);
        // current_array_depth end must add 1 after assignment
        // because current_array_depth always save that next array position
        this->current_array_depth.back() += 1;
        // carry a number after current_array_depth
        // depth_index is that including all size of array after depth_index
        // so if depth[depth_index] == array.depth_size_at(depth_index)
        // depth[depth_index] is full, should exit, otherwize panic(array outmemory)
        // follow code will use that
        for(int i = this->current_array_depth.size()-1; i > this->current_array_depth_index ; --i) {
          if(this->current_array_depth.at(i) >= this->current_array->depth_size_at(i)) {
            this->current_array_depth[i] = 0;
            this->current_array_depth[i-1]++;
          }
        }
      } else if(auto * array = get_if<unique_ptr<ast::NodeArrayInit>>(&init_item)) {
        // maintain current_array_depth_index
        this->current_array_depth_index++;
        (*array)->accept(*this);
        // maintain current_array_depth_index
        this->current_array_depth_index--;
        // carry a number after array_init
        // exmple: [2][2]: [1][2] -> [2][0], depth_index: 1 -> 0
        for(int i = this->current_array_depth.size()-1; i > this->current_array_depth_index ; --i) {
          if(this->current_array_depth.at(i) >= this->current_array->depth_size_at(i)) {
            this->current_array_depth[i] = 0;
            this->current_array_depth[i-1]++;
          }
        }
      } else {
        Panic("undefined array_init");
      }
    }
    // default setting 0
    // check index array size and set 0
    while(this->current_array_depth.at(this->current_array_depth_index) <
        this->current_array->depth_size_at(this->current_array_depth_index)) {
      // generate zero register for follow
      Reg * zero_reg = this->genarator->gen_inst_imm(*(this->current_block), VarType::i32, 0);
      // carray all bit befor current_array_depth
      for(int i = this->current_array_depth.size()-1; i >= 0; --i) {
        if(this->current_array_depth.at(i) >= this->current_array->depth_size_at(i)) {
          Assert(i != 0, "zero error: array too long:%d", this->current_array_depth.at(i));
          this->current_array_depth[i] = 0;
          this->current_array_depth[i-1]++;
        }
      }
      Reg * index_reg = this->genarator->gen_inst_imm(*(this->current_block),
          VarType::i32, this->current_array->get_single_index(this->current_array_depth));
      this->genarator->gen_inst_array_store(*(this->current_block),
          this->current_array_reg, zero_reg, index_reg);
      this->current_array_depth.back() += 1;
      // carray bit after current_array_depth
      for(int i = this->current_array_depth.size()-1; i > this->current_array_depth_index; --i) {
        if(this->current_array_depth.at(i) >= this->current_array->depth_size_at(i)) {
          this->current_array_depth[i] = 0;
          this->current_array_depth[i-1]++;
        }
      }
    }
  }

  void Visitor::visit(ast::NodeFuncDef & func_def) {
    this->current_func = this->genarator->create_func(func_def.iden, func_def.type);
    this->iden_stack.push(func_def.iden, this->current_func);
    this->iden_stack.new_stack();
    if(func_def.stage == ast::NodeFuncDef::HAVE_ARGS) {
      func_def.func_def_args->accept(*this);
    }
    this->current_block = this->genarator->create_begin_block(*(this->current_func));
    func_def.block->accept(*this);
    this->iden_stack.delete_last_stack();
  }

  void Visitor::visit(ast::NodeFuncDefArgs & func_def_args) {
    for(uint64_t i = 0; i < func_def_args.iden_list.size(); ++i) {
      Reg * reg = this->genarator->gen_inst_arg_alloc(*(this->current_func),
          func_def_args.type_list.at(i));
      this->iden_stack.push(func_def_args.iden_list.at(i), reg);
    }
  }

  void Visitor::visit(ast::NodeFuncCall & func_call) {
    IdenStackResult iden_stack_result = this->iden_stack.get(func_call.iden);
    if(iden_stack_result.tag == IdenStackResult::FUNCTION) {
      // get args
      if(func_call.func_args.has_value()) {
        func_call.func_args.value()->accept(*this);
      }
      // correct args type
      std::vector<Reg*> arg_list;
      Assert(this->current_args.size() == iden_stack_result.func->arg_list.size(),
          "func call args number:%zu not same to func args number:%zu",
          this->current_args.size(), iden_stack_result.func->arg_list.size());
      for(unsigned long i = 0; i < this->current_args.size(); ++i) {
        Reg * arg_reg = confirm_ptr_type(iden_stack_result.func->arg_list.at(i)->get_type(),
            this->current_args.at(i));
        arg_list.push_back(arg_reg);
      }
      this->current_func_call_reg = this->genarator->gen_inst_call(
          *(this->current_block), iden_stack_result.func, arg_list);
    } else {
      Panic("iden return tag must be func:%s",
          enum_name(iden_stack_result.tag).c_str());
    }
  }

  void Visitor::visit(ast::NodeBlock & block) {
    block.block_item_list->accept(*this);
  }

  void Visitor::visit(ast::NodeBlockItems & block_items) {
    for(const auto & const_decl : block_items.const_decl_list) {
      const_decl->accept(*this);
    }
    for(const auto & var_decl : block_items.var_decl_list) {
      var_decl->accept(*this);
    }

    for(const auto & stmt : block_items.stmt_list) {
      stmt->accept(*this);
    }
  }

  void Visitor::visit(ast::NodeStmt & stmt) {
    switch(stmt.stage) {
      case ast::NodeStmt::Stage::ASSIGN:
        stmt.stmt_assign->accept(*this);
        break;
      case ast::NodeStmt::Stage::RETURN:
        stmt.stmt_return->accept(*this);
        break;
      case ast::NodeStmt::Stage::IF:
        stmt.stmt_if->accept(*this);
        break;
      case ast::NodeStmt::Stage::WHILE:
        stmt.stmt_while->accept(*this);
        break;
      case ast::NodeStmt::Stage::BREAK:
        stmt.stmt_break->accept(*this);
        break;
      case ast::NodeStmt::Stage::CONTINUE:
        stmt.stmt_continue->accept(*this);
        break;
      case ast::NodeStmt::Stage::FUNCCALL:
        stmt.stmt_func_call->accept(*this);
        break;
      case ast::NodeStmt::Stage::BLOCK:
        stmt.block->accept(*this);
        break;
      default: Panic("stmt stage undefined");
    }
  }

  void Visitor::visit(ast::NodeStmtAssign & stmt_assign) {
    Reg * array_index_reg = nullptr;
    if(stmt_assign.stage == ast::NodeStmtAssign::ARRAY) {
      // get array_reg
      stmt_assign.array->accept(*this);
      array_index_reg = this->current_array_single_index_reg;
    }
    stmt_assign.exp->accept(*this);
    IdenStackResult iden_stack_result = this->iden_stack.get(stmt_assign.iden);
    switch(iden_stack_result.tag) {
    case IdenStackResult::NONE:
      Panic("can't find iden:%s", stmt_assign.iden.c_str());
      break;
    case IdenStackResult::REG:
      this->genarator->gen_inst_store(*(this->current_block),
        iden_stack_result.reg, this->current_exp_reg);
      break;
    case IdenStackResult::INT_ARRAY:
      if(stmt_assign.stage == ast::NodeStmtAssign::ARRAY) {
        this->genarator->gen_inst_array_store(*(this->current_block),
            iden_stack_result.reg, this->current_exp_reg, array_index_reg);
      } else {
        Panic("must array");
      };
      break;
    case IdenStackResult::GLOBAL_REG:
      this->genarator->gen_inst_global_store(*(this->current_block),
          iden_stack_result.global_reg, this->current_exp_reg);
      break;
    case IdenStackResult::INT:
      Panic("assign can't use const value");
      break;
    default:
      Panic("undefined stack result");
    }
  }

  void Visitor::visit(ast::NodeStmtReturn & stmt_return) {
    if(stmt_return.exp.has_value()) {
      stmt_return.exp.value()->accept(*this);
      // transform type
      Reg * reg = confirm_type(this->current_func->func_type, this->current_exp_reg);
      this->genarator->gen_inst_ret(*(this->current_block),
          this->current_func->func_type, reg);
    } else {
      this->genarator->gen_inst_ret(*(this->current_block), VarType::none, nullptr);
    }
  }

  void Visitor::visit(ast::NodeStmtIf & stmt_if) {
    if(stmt_if.stage == ast::NodeStmtIf::Stage::NO_ELSE) {
      Block * skip_block = this->genarator->create_block(*(this->current_func));
      this->current_cond_block = skip_block;
      stmt_if.cond->accept(*this);
      stmt_if.stmt_true->accept(*this);
      this->current_block = skip_block;
    } else if(stmt_if.stage == ast::NodeStmtIf::Stage::ELSE) {
      Block * false_block = this->genarator->create_block(*(this->current_func));
      Block * skip_block = this->genarator->create_block(*(this->current_func));
      this->current_cond_block = false_block;
      stmt_if.cond->accept(*this);
      stmt_if.stmt_true->accept(*this);
      this->genarator->gen_inst_jmp(*(this->current_block), skip_block);
      this->current_block = false_block;
      stmt_if.stmt_false->accept(*this);
      this->current_block = skip_block;
    } else {
      Panic("stmt if stage undefined");
    }
  }

  void Visitor::visit(ast::NodeStmtWhile & stmt_while) {
    Block * begin_block = this->genarator->create_block(*(this->current_func));
    Block * end_block = this->genarator->create_block(*(this->current_func));
    this->while_begin_block.push(begin_block);
    this->while_end_block.push(end_block);
    this->current_block = begin_block;
    this->current_cond_block = end_block;
    stmt_while.cond->accept(*this);
    stmt_while.stmt->accept(*this);
    this->genarator->gen_inst_jmp(*(this->current_block), begin_block);
    this->current_block = end_block;
    this->while_begin_block.pop();
    this->while_end_block.pop();
  }

#if defined(__GNUC__)
  void Visitor::visit(__attribute__((unused)) ast::NodeStmtBreak & stmt_break) {
#else
  void Visitor::visit(ast::NodeStmtBreak & stmt_break) {
#endif
    Assert(this->while_begin_block.empty() == false, "not in a while block");
    this->genarator->gen_inst_jmp(*(this->current_block),
        this->while_begin_block.top());
  }

#if defined(__GNUC__)
  void Visitor::visit(__attribute__((unused)) ast::NodeStmtContinue & stmt_continue) {
#else
  void Visitor::visit(ast::NodeStmtContinue & stmt_continue) {
#endif
    Assert(this->while_end_block.empty() ==  false, "not in a while block");
    this->genarator->gen_inst_jmp(*(this->current_block),
        this->while_end_block.top());
  }

  void Visitor::visit(ast::NodeStmtFuncCall & stmt_func_call) {
    stmt_func_call.func_call->accept(*this);
  }

  // set current_exp_reg
  void Visitor::visit(ast::NodeExp & exp) {
    if(exp.stage == ast::NodeExp::EXP) {
      exp.exp1->accept(*this);
      Reg * exp_reg1 = this->current_exp_reg;
      exp.exp2->accept(*this);
      Reg * exp_reg2 = this->current_exp_reg;
      Reg * reg1 = nullptr;
      Reg * reg2 = nullptr;
      Reg * res = nullptr;
      if(exp_reg1->get_type() == VarType::i32 && exp_reg2->get_type() == VarType::f32) {
        reg1 = this->genarator->gen_inst_i32_to_f32(*(this->current_block), exp_reg1);
        reg2 = exp_reg2;
      } else if(exp_reg1->get_type() == VarType::f32 && exp_reg2->get_type() == VarType::i32) {
        reg1 = exp_reg2;
        reg2 = this->genarator->gen_inst_i32_to_f32(*(this->current_block), exp_reg2);
      } else {
        reg1 = exp_reg1;
        reg2 = exp_reg2;
      }

      switch(exp.op_type) {
      case OpType::ADD:
        res = this->genarator->gen_inst_add(*(this->current_block),
            reg1, reg2);
        break;
      case OpType::SUB:
        res = this->genarator->gen_inst_sub(*(this->current_block),
            reg1, reg2);
        break;
      case OpType::MUL:
        res = this->genarator->gen_inst_mul(*(this->current_block),
            reg1, reg2);
        break;
      case OpType::DIV:
        res = this->genarator->gen_inst_div(*(this->current_block),
            reg1, reg2);
        break;
      default: Panic("op_type undefined");
      }
      // set current_exp_reg
      this->current_exp_reg = res;
    } else if(exp.stage == ast::NodeExp::BRAC_EXP) {
      exp.exp1->accept(*this);
    } else if(exp.stage == ast::NodeExp::INT_NUMBER) {
      Reg * res = this->genarator->gen_inst_imm(*(this->current_block),
          VarType::i32, exp.int_number);
      this->current_exp_reg = res;
    } else if(exp.stage == ast::NodeExp::FLOAT_NUMBER) {
      Reg * res = this->genarator->gen_inst_imm(*(this->current_block),
          VarType::f32, exp.float_number);
      this->current_exp_reg = res;
    } else if(exp.stage == ast::NodeExp::IDEN) {
      Reg * res = nullptr;

      IdenStackResult iden_stack_result = this->iden_stack.get(exp.iden);
      if(iden_stack_result.tag == IdenStackResult::REG) {
        res = this->genarator->gen_inst_load(*(this->current_block),
            iden_stack_result.reg);
      } else if(iden_stack_result.tag == IdenStackResult::INT) {
        res = this->genarator->gen_inst_imm(*(this->current_block),
            VarType::i32, iden_stack_result.int_number);
      } else if(iden_stack_result.tag == IdenStackResult::GLOBAL_REG) {
        res = this->genarator->gen_inst_global_load(*(this->current_block),
            iden_stack_result.global_reg);
      } else {
        Panic("exp stmt must use reg iden");
      }
      this->current_exp_reg = res;
    } else if(exp.stage == ast::NodeExp::ARRAY) {
      IdenStackResult iden_stack_result = this->iden_stack.get(exp.iden);
      if(iden_stack_result.tag == IdenStackResult::INT_ARRAY) {
        // set current_array for NodeArray accept
        this->current_array = iden_stack_result.int_array.array_info;
        Reg * array_reg = iden_stack_result.int_array.reg;

        // get current_array_single_index_reg
        exp.array->accept(*this);
        Assert(this->current_array_single_index_reg != nullptr,
            "current_array_single_index_reg is nullptr");
        Reg * index = this->current_array_single_index_reg;

        Reg * res = this->genarator->gen_inst_array_load(*(this->current_block),
            array_reg, index);
        // set current_exp_reg
        this->current_exp_reg = res;
      } else {
        Panic("iden result tag must be int array:%s",
            static_cast<std::string>(magic_enum::enum_name(iden_stack_result.tag)).c_str());
      }
    } else if(exp.stage == ast::NodeExp::FUNC) {
      // get call_func_reg
      exp.func_call->accept(*this);
      this->current_exp_reg = this->current_func_call_reg;
    } else {
      Panic("exp stage undefined");
    }
  }

  // generate current_args
  void Visitor::visit(ast::NodeFuncArgs & func_args) {
    // clear current_args
    this->current_args.clear();
    // get current_exp_reg
    for(auto & exp : func_args.exp_list) {
      exp->accept(*this);
      this->current_args.push_back(this->current_exp_reg);
    }
  }

  void Visitor::visit(ast::NodeCond & cond) {
    Reg * rs1 = nullptr;
    Reg * rs2 = nullptr;
    switch(cond.exp->stage) {
      case ast::NodeExp::Stage::EXP: {
        cond.exp->exp1->accept(*this);
        rs1 = this->current_exp_reg;
        cond.exp->exp2->accept(*this);
        rs2 = this->current_exp_reg;
        switch(cond.exp->op_type) {
        case OpType::EEQ:
          this->genarator->gen_inst_bne(*(this->current_block),
              rs1, rs2, this->current_cond_block);
          break;
        case OpType::NEQ:
          this->genarator->gen_inst_beq(*(this->current_block),
            rs1, rs2, this->current_cond_block);
          break;
        default:
          Panic("cond can't use this op type:%s",
              op_type_to_str(cond.exp->op_type).c_str());
        }
        break;
      case ast::NodeExp::Stage::INT_NUMBER: {
        rs1 = this->genarator->gen_inst_imm(*(this->current_block), VarType::i32, 0);
        rs2 = this->genarator->gen_inst_imm(*(this->current_block), VarType::i32,
            cond.exp->int_number);
        this->genarator->gen_inst_beq(*(this->current_block), rs1, rs2,
            this->current_cond_block);
      }
        break;
      }
      default:
        Panic("cond stage undefined");
    }
  }

  Reg * Visitor::confirm_type(VarType reference_type, Reg * src) {
    Reg * res = src;
    if(reference_type == VarType::i32 && src->get_type() == VarType::f32) {
      res = this->genarator->gen_inst_f32_to_i32(*(this->current_block), src);
    } else if(reference_type == VarType::f32 && src->get_type() == VarType::i32) {
      res = this->genarator->gen_inst_i32_to_f32(*(this->current_block), src);
    }
    return res;
  }

  Reg * Visitor::confirm_ptr_type(VarType reference_ptr_type, Reg * src) {
    Reg * res = src;
    if(reference_ptr_type == VarType::ptr_i32 && src->get_type() == VarType::f32) {
      res = this->genarator->gen_inst_f32_to_i32(*(this->current_block), src);
    } else if(reference_ptr_type == VarType::ptr_f32 && src->get_type() == VarType::i32) {
      res = this->genarator->gen_inst_i32_to_f32(*(this->current_block), src);
    }
    return res;
  }
}
